package cz.vse.adventura.po1715.xkosd00_kostka.hra;

import cz.pecinovsky.english.pojj.adv_framework.game_gui.IListener;
import cz.pecinovsky.english.pojj.adv_framework.game_gui.IPlaceG;
import java.awt.Point;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static cz.vse.adventura.po1715.xkosd00_kostka.hra.Predmet.TEZKY;
import static cz.vse.adventura.po1715.xkosd00_kostka.hra.Texty.*;


/*******************************************************************************
 * Instance třídy {@code Mistnost} představují jednotlive mistnosti
 * v prochazenem byte. Trida {@code Mistnost} je soucasne schopna vybudovat
 * na zákadě zadných parametrů cely byt z jednotlivych mistnosti.
 *
 * @author    Dominik KOSTKA
 * @version   1.0
 */
public enum Mistnost implements IPlaceG
{
//== HODNOTY VÝČTOVÉHO TYPU ====================================================

    /** Vstupní mistnost bytu odkud  se dá přesunout do většiny místností. */
    Hala ( new Point(100,310),
        "Vstupní místnost hostelu ve které hra začíná.",
        new String[] { RECEPCE, WC },
        TEZKY+INFORMAČNÍ_TABULE
//        new String[] { "Ložnice", "Obývák", "Koupelna" },
//        TEZKY+"Botník", "Deštník"
    ),

    /** Tajná místnost ve hře. */
    Zkratka ( new Point(0,0),
        "Tajná místnost ve hře.",
        new String[] {}
    ),

    /** Veřejný záchod */
    Wc ( new Point(25,400),
        "Veřejný záchod.",
        new String[] { HALA, TOALETA },
        TEZKY+UMYVADLO, MÝDLO, TOALETNÍ_PAPÍR, FÉN, TEZKY+ZRCADLO
//        new String[] { "Hala" },
//        "Brýle", TEZKY+"Umyvadlo", "Časopis"
    ),

    /** Kabinka na Wc. */
    Toaleta ( new Point(100,400),
        "Kabinka na WC.",
        new String[] { WC }
        
//        new String[] { "Hala" },
//        "Brýle", TEZKY+"Umyvadlo", "Časopis"
    ),
//
    /** Mistnost, kde se lze registrovat.*/
    Recepce ( new Point(440,310),
        "Místnost, kde se lze registrovat.",
        new String[] { HALA, VÝTAH },
        TEZKY+RECEPČNÍ
//        new String[] { "Kuchyň", "Hala" },
//        TEZKY+"Postel", TEZKY+"Zrcadlo", "Župan"
    ),

    /** Osoba u které se lze registrovat.*/
    Recepční( new Point(440,310),
        "Osoba u které se lze registrovat.",
        new String[] { RECEPCE }
//        new String[] { "Kuchyň", "Hala" },
//        TEZKY+"Postel", TEZKY+"Zrcadlo", "Župan"
    ),

    /** Mistnost, sloužící k přesunu do jednotlivých pater. */
    Výtah ( new Point(570,200),
        "Místnost, sloužící k přesunu do jednotlivých pater.",
        new String[] { RECEPCE, PATRO1, PATRO2 },
        TEZKY+ZRCADLO, ŽIDLE
//        new String[] { "Kuchyň", "Hala" },
//        TEZKY+"Televize"
    ),

    /** Židle.*/
    Židle ( new Point(580,30),
        "Židle ve výtahu.",
        new String[] {  }
//        new String[] { "Kuchyň", "Hala" },
//        TEZKY+"Televize"
    ),

    
    Patro1 ( new Point(400,220),
        "První patro hostelu.",
        new String[] { VÝTAH, KOUPELNA1, POKOJ11, POKOJ12, POKOJ13, POKOJ14, POKOJ15}, TEZKY + OKNO1, TEZKY + VYPÍNAČ1),

    
    Patro2 ( new Point(400,75),
        "Druhé patro hostelu.",
        new String[] { VÝTAH, KOUPELNA2, POKOJ21, POKOJ22, POKOJ23,
    POKOJ24, POKOJ25},
        TEZKY+OKNO2, TEZKY+VYPÍNAČ2
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    
    Koupelna1 ( new Point(20,220),
        "Koupelna v 1. patře.",
        new String[] { PATRO1},
        TEZKY+SPRCHA, TEZKY+UMYVADLO
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    , MÝDLO, FÉN, TEZKY + ZRCADLO, TEZKY + ZÁCHOD),

    
    Koupelna2 ( new Point(20,75),
        "Koupelna ve 2. patře.",
        new String[] { PATRO2},
        TEZKY+SPRCHA, TEZKY+UMYVADLO, TOALETNÍ_PAPÍR,
        FÉN, TEZKY+ZRCADLO, TEZKY+ZÁCHOD
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    

   
    Okno1 ( new Point(130,220),
        "Okno v 1.patře.",
        new String[] {  }
    ),

    /** */
    Okno2 ( new Point(130,75),
        "Okno v 2.patře.",
        new String[] {}
//       new String[] {},
//        "@Pivo", "@Pivo", "@Pivo", "Salám", "Houska", "@Víno", "@Rum"
    ),



    /**.*/
    Pokoj11 ( new Point(100,150),
        "Pokoj č. 11.",
        new String[] {PATRO1}
//       new String[] {},
//        "@Pivo", "@Pivo", "@Pivo", "Salám", "Houska", "@Víno", "@Rum"
    , TEZKY + POSTEL, TEZKY + STOLEK, TEZKY + SKŘÍŇ),

    /***/
    Pokoj12 ( new Point(180,150),
        "Pokoj č. 12.",
        new String[] { PATRO1 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj13 ( new Point(250,150),
        "Pokoj č. 13.",
        new String[] { PATRO1 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj14 ( new Point(330,150),
        "Pokoj č. 14.",
        new String[] { PATRO1 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /***/
    Pokoj15 ( new Point(400,150),
        "Pokoj č. 15.",
        new String[] { PATRO1 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /***/
    Pokoj21 ( new Point(100,0),
        "Pokoj č. 21.",
        new String[] { PATRO2 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj22 ( new Point(180,0),
        "Pokoj č. 22.",
        new String[] { PATRO2 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj23 ( new Point(250,0),
        "Pokoj č. 23.",
        new String[] { PATRO2 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj24 ( new Point(330,0),
        "Pokoj č. 24.",
        new String[] { PATRO2 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    ),

    /** */
    Pokoj25 ( new Point(400,0),
        "Pokoj č. 25.",
        new String[] { PATRO2 },
        TEZKY+POSTEL, TEZKY+STOLEK, TEZKY+SKŘÍŇ
//        new String[] { "Ložnice", "Obývák" },
//        TEZKY+"Lednička", "Papír"
    );

//== KONSTANTNÍ ATRIBUTY TŘÍDY =================================================

    /** Mapa sloužící k převodu názvu místnosti na odpovídající místnost. */
    private static final Map<String,Mistnost> název2místnost =
                                              new HashMap<String,Mistnost>();

    /** Posluchači očekávající oznámení změně předmětů v místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiPředmětů =
                                            new ArrayList<IListener<IPlaceG>>();

    /** Posluchači očekávající oznámení změně sousedů místnosti. */
    private static final List<IListener<IPlaceG>> posluchačiSousedů =
                                            new ArrayList<IListener<IPlaceG>>();

//== PROMĚNNÉ ATRIBUTY TŘÍDY ===================================================

    private static Mistnost aktuálníMístnost;



//== STATICKÝ INICIALIZAČNÍ BLOK - STATICKÝ KONSTRUKTOR ========================

    //Teprve nyní existují všechny instance i všechny potřebné kontejnery,
    //a proto lze teprve nyní inicializovat sousedy místnosti
    static {
        //Zapamatuji si jména všech místností malými písmeny
        for (Mistnost m : values()) {
            název2místnost.put(m.name().toLowerCase(), m);
        }
        //Jména výchozích sousedů převedu na sousedy a ty uložím
        for (Mistnost m : values()) {
            for (int i=0;   i < m.názvyVýchozíchSousedů.length;   i++) {
                m.výchozíSousedé[i] = název2místnost.get(
                                      m.názvyVýchozíchSousedů[i].toLowerCase());
            }
            m.názvyVýchozíchSousedů = null;  //Už nebudou potřeba
        }
        inicializuj();
    }



//== KONSTANTNÍ ATRIBUTY INSTANCÍ ==============================================

    /** Popis významu a účelu dané místnosti. */
    private final String popis;

    /** Sousedé místnosti na začátku hry. */
    private final Mistnost[] výchozíSousedé;

    /* Předměty v místnosti na počátku hry. */
     private final Predmet[] výchozíPředměty;

    /** Aktuální sousedé dané místnosti. */
    private final Collection<Mistnost> sousedé  = new ArrayList<Mistnost>();

    /** Předměty nacházející se aktuálně v místnosti. */
    private final Collection<Predmet>  předměty = new ArrayList<Predmet>();

    /** Pozice místnosti na plánku hry. */
    private final Point pozice;



//== PROMĚNNÉ ATRIBUTY INSTANCÍ ================================================

    /** Pomocná proměnná používaná v konstruktoru třídy
     *  pro inicializaci pole výchozíSousedé. */
    private String[] názvyVýchozíchSousedů;



//== PŘÍSTUPOVÉ METODY ATRIBUTŮ TŘÍDY ==========================================

    /***************************************************************************
     * Vrátí odkaz na aktuálni místnost,
     * tj. na místnost, v níž se právě nachází hráč.
     *
     * @return Požadovaný odkaz
     */
    static Mistnost getAktuálníMístnost()
    {
        return aktuálníMístnost;
    }


    /***************************************************************************
     * Nastaví zadanou místnost jako aktuální.
     *
     * @param soused Nastavovaná aktuální místnost
     */
    static void setAktuálníMístnost(Mistnost místnost) {
        aktuálníMístnost = místnost;
        obvolejPosluchače();
    }


    /***************************************************************************
     * Vrátí odkaz na místnost se zadaným názvem.
     *
     * @param název Název hledané místnosti
     * @return Požadovaný odkaz
     */
    static Mistnost getMístnost(String název)
    {
        Mistnost cil = název2místnost.get(název.toLowerCase());
        return cil;
    }


    /***************************************************************************
     * Vrátí kolekci odkazů na jednotlivé místnosti.
     *
     * @return Požadovaná kolekce
     */
    static Collection<Mistnost> getMístnosti()
    {
        return new ArrayList<Mistnost>(Arrays.asList(values()));
    }



//== OSTATNÍ NESOUKROMÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Inicializuje místnosti do výchozího stavu pro spuštění hry,
     * tj. nastavuje jim výchozí sousedy a výchozí přítomné předměty.
     * Metoda přitom předpokládá, že jsou již přihlášeni všichni posluchači.
     */
    static void inicializuj()
    {
        for (Mistnost m : values()) {
            m.sousedé.clear();
            m.sousedé.addAll(Arrays.asList(m.výchozíSousedé));
            m.předměty.clear();
            m.předměty.addAll(Arrays.asList(m.výchozíPředměty));
        }
        aktuálníMístnost = Hala;
        obvolejPosluchače();
    }

    /***************************************************************************
     * Obvolá všechny přihlášené posluchače zadaného stavu,
     * tj. posluchače předmětů i posluchače sousedů,
     * a tím jim oznámí, že se množina sledovaných objektů změnila.
     */
    static void obvolejPosluchače()
    {
        obvolejPosluchače(posluchačiPředmětů);
        obvolejPosluchače(posluchačiSousedů);
    }

    /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.add(posluchač);
    }

    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny předmětů v místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačePředmětů(IListener<IPlaceG> posluchač)
    {
        posluchačiPředmětů.remove(posluchač);
    }

    /***************************************************************************
     * Přidá zadaného posluchače do seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Přihlašovaný posluchač
     */
    static void přihlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.add(posluchač);
    }

    /***************************************************************************
     * Odebere zadaného posluchače ze seznamu posluchačů,
     * které zpravuje o změně množiny sousedů místnosti
     *
     * @param posluchač Odhlašovaný posluchač
     */
    static void odhlašPosluchačeSousedů(IListener<IPlaceG> posluchač)
    {
        posluchačiSousedů.remove(posluchač);
    }



//##############################################################################
//== KONSTRUKTORY A TOVÁRNÍ METODY =============================================

    /***************************************************************************
     * Vytvoří mistnost se zadanými charakteristikami, popisem účelu místnosti,
     * pozicí případného návštěvníka (= pozicí na plánku),
     * seznamem možných sousedů (kam všude se dá z místnosti jít) a
     * seznamem v místnosti se vyskytujících předmětů.
     *
     * @param popis    Popis účelu dané místnosti
     * @param sousedé  Pole s názvy sousedů dané místnosti, tj. s názvy
     *                 místností, do nichž se dá z dané místnosti přejít.
     * @param předměty Názvy předmětů, které se v místnosti vyskytují.
     *                 Konstruktor nechá tyto předměty vyrobit.
     */
    private Mistnost(Point pozice, String popis,
                     String[] sousedé, String... předměty)
    {
        this.pozice = pozice;
        this.popis  = popis;
        this.názvyVýchozíchSousedů = sousedé;
        this.výchozíSousedé  = new Mistnost[sousedé.length];
        this.výchozíPředměty = new Predmet [předměty.length];
        for (int i=0;   i < předměty.length;   i++ ) {
            Predmet předmět = new Predmet(předměty[i]);
            výchozíPředměty[i] = předmět;
        }
    }



//== ABSTRAKTNÍ METODY =========================================================
//== PŘÍSTUPOVÉ METODY INSTANCÍ ================================================

    /***************************************************************************
     * Nastaví pro místnost nového zadaného souseda
     * @param   soused nový soused Výtahu. Povolené hodnoty parametru jsou
     *          TRecepce, Patro2 a Patro1
     */


    /***************************************************************************
     * Vrati název mistnosti.
     *
     * @return Nazev mistnosti
     */
    @Override
    public String getName()
    {
        return name();
    }


    /***************************************************************************
     * Vrati popis mistnosti, ktery danou mistnost strucne charakterizuje.
     *
     * @return Pozadovany popis
     */
    @Override
    public String getDescription()
    {
        return popis;
    }


    /***************************************************************************
     * Vrati kolekci predmetu nachazejicich se v dane mistnosti.
     *
     * @return Pozadovana kolekce
     */
    @Override
    public Collection<Predmet> getObjects()
    {
        return Collections.unmodifiableCollection(předměty);
    }


    /***************************************************************************
     * Přidá souseda do kolekce sousedů místnosti
     *
     * @return Pozadovana kolekce
     */
    public void setSoused(Mistnost soused)
    {
        sousedé.add(soused);
        obvolejPosluchače(posluchačiSousedů);
    }

    /***************************************************************************
     * Vrati kolekci mistnosti, do nichz je mozno se z teto mistnosti presunout.
     *
     * @return Kopie pozadovane kolekce
     */
    @Override
    public Collection<Mistnost> getNeighbors()
    {
        return new ArrayList<Mistnost>(sousedé);
    }

    /***************************************************************************
     * Vrátí pozici místnosti na plánu hry,
     * presněji pozici hráče nacházejícícho se v dané místnosti.
     * Na této pozici bude hráč při pobytu v dané místnosti zobrazen.
     *
     * @return Požadovaná pozice
     */
    @Override
    public Point getPosition()
    {
        return pozice;
    }


//== OSTATNÍ NESOUKROMÉ  METODY INSTANCÍ =======================================

    /***************************************************************************
     * Vrátí název dané instance.
     *
     * @return Nazev instance
     */
    @Override
    public String toString()
    {
        return name();
    }


    /***************************************************************************
     * Odlozi zadany predmet v mistnosti.
     *
     * @param  predmet odkladany v mistnosti
     * @return {@code true} pokud byl predmet do mistnosti vlozen
     */

    void poloz( Predmet predmet ) {
        předměty.add( predmet );
        obvolejPosluchače(posluchačiPředmětů);
    }


    /***************************************************************************
     * Odebere predmet zadaneho nazvu z mistnosti.
     * Odebrat je mozne pouze zvednutelné předměty.
     *
     * @param název Nazev odebiraneho predmetu
     * @return Byl-li předmět z místnosti odebrán, vrátí tento předmět.
     *         Nebyl-li předmět zvednutelný, vrátí předmět {@link TÍHA}.
     *         Nebyl-li v místnosti požadovaný předmět, vrátí {@code null}.
     */
    Predmet zvedni(String nazev)
    {
        String jmeno;
        Predmet nasel = null;
        for( Predmet predmet : předměty ) {
            jmeno = predmet.getName();
            if (jmeno.equalsIgnoreCase( nazev ))  {
                if (predmet.isTransportable())  {
                    předměty.remove(predmet);
                    obvolejPosluchače(posluchačiPředmětů);
                    nasel = predmet;
                } else {
                    nasel = Predmet.TÍHA;
                }
                break;
            }
        }
        return nasel;
    }

    



//== SOUKROMÉ A POMOCNÉ METODY TŘÍDY ===========================================

    /***************************************************************************
     * Obvolá všechny přihlášené posluchače zadaného stavu, čímž jím oznámí,
     * že se množina sledovaných objektů (možná) změnila.
     *
     * @param posluchači Seznam obvolávaných posluchačů
     */
    private static void obvolejPosluchače(
                        Collection<IListener<IPlaceG>> posluchači)
    {
        for ( IListener<IPlaceG> posluchač : posluchači) {
            posluchač.notice(aktuálníMístnost);
        }
    }

//== SOUKROMÉ A POMOCNÉ METODY INSTANCÍ ========================================
//== VNOŘENÉ A VNITŘNÍ TŘÍDY ===================================================
//== TESTOVACÍ METODY A TŘÍDY ==================================================
//
//     /***************************************************************************
//      * Testovací metoda.
//      */
//     public static void test()
//     {
//     }
//     /** @param args Parametry příkazového řádku - nepoužité. */
//     public static void main( String... args ) { test(); }
}
